

#region Using Statements
using System;
using System.IO;
using System.Collections.Generic;//using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Net;
#endregion



namespace Pauliver
{
    [Serializable]
    public struct TerrainSaver
    {
        public int Square_Height;
        public int Square_Width; //width +-3
        public int Triangle_Height;
        public int Triangle_Width; //width +-2
        public int Noise1_Height;
        public int Noise1_Width; //width  +- 10
        public int Noise2_Height;
        public int Noise2_Width; //width +- 5
        public int Offset_From_Zero;

        public void Write(System.IO.StreamWriter sw)
        {
            sw.WriteLine("  " + Square_Height + "," + Square_Width + "," + Triangle_Height + "," + Triangle_Width + "," + Noise1_Height + "," + Noise1_Width + "," + Noise2_Height + "," + Noise2_Width + "," + Offset_From_Zero);
        }

        public bool Read(System.IO.StreamReader sr)
        {
            string data = "";
            try
            {
                data = sr.ReadLine();
            }catch(Exception)
            {
                return false;
            }
            if (!data.Contains(","))
                return false;

            Square_Height = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Square_Width = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Triangle_Height = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Triangle_Width = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Noise1_Height = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Noise1_Width = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Noise2_Height = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Noise2_Width = Convert.ToInt32(data.Substring(0, data.IndexOf(",")));
            data = data.Substring(data.IndexOf(",") + 1);

            Offset_From_Zero = Convert.ToInt32(data);
            //sw.WriteLine("  " + Square_Height + "," + Square_Width + "," + Triangle_Height + "," + Triangle_Width + "," + Noise1_Height + "," + Noise1_Width + "," + Noise2_Height + "," + Noise2_Width + "," + Offset_From_Zero);
            return true;
        }
    
        public void Write(PacketWriter pw)
        {
            pw.Write(Square_Height);
            pw.Write(Square_Width);
            pw.Write(Triangle_Height);
            pw.Write(Triangle_Width);
            pw.Write(Noise1_Height);
            pw.Write(Noise1_Width);
            pw.Write(Noise2_Height);
            pw.Write(Noise2_Width);
            pw.Write(Offset_From_Zero);
        }
        public void Read(PacketReader pr)
        {
            Square_Height = pr.ReadInt32();
            Square_Width = pr.ReadInt32();
            Triangle_Height = pr.ReadInt32();
            Triangle_Width = pr.ReadInt32();
            Noise1_Height = pr.ReadInt32();
            Noise1_Width = pr.ReadInt32();
            Noise2_Height = pr.ReadInt32();
            Noise2_Width = pr.ReadInt32();
            Offset_From_Zero = pr.ReadInt32();
        }
        public int[] Values
        {
            get
            {
                int[] values = new int[9];
                values[0] = Square_Height;
                values[1] = Square_Width;
                values[2] = Triangle_Height;
                values[3] = Triangle_Width;
                values[4] = Noise1_Height;
                values[5] = Noise1_Width;
                values[6] = Noise2_Height;
                values[7] = Noise2_Width;
                values[8] = Offset_From_Zero;
                return values;
            }
            set
            {
                if (value.Length < 9)
                {
                    throw new ArgumentException("Integer Array must be 9 args long");
                }else{
                    Square_Height = value[0];
                    Square_Width = value[1];
                    Triangle_Height = value[2];
                    Triangle_Width = value[3];
                    Noise1_Height = value[4];
                    Noise1_Width = value[5];
                    Noise2_Height = value[6];
                    Noise2_Width = value[7];
                    Offset_From_Zero = value[8];
                }
            }
        }
    }

    public class Terrain
    {   
        public static Color BackGround = new Color(241, 185, 71, 256);// no alpha

        int[] Heights;
        public Texture2D BackgroundLeft;
        public Texture2D BackgroundRight;
        public Texture2D m_MiniMap;
        public Color[] m_TextureData;
        public Matrix m_Transformation;
        public Rectangle m_BoundingRectangle;
        public int tolerance;
        public static int EdgeOffset = 120;

        public Terrain(Texture2D p_BackgroundLeft, Texture2D p_BackgroundRight, Texture2D p_MiniMap, TerrainSaver ts)
        {
#if WINDOWS
            GraphicsDevice gd = p_MiniMap.GraphicsDevice;
            int height = p_MiniMap.Height;
            int width = p_MiniMap.Width;
            p_MiniMap.Dispose();

            p_MiniMap = new Texture2D(gd, width, height);
            PContentManager.Instance.SaveContent<Texture2D>(p_MiniMap, "MiniMap");
#endif

            int s_hr = ts.Square_Height;
            int s_wr = ts.Square_Width;
            int t_hr = ts.Triangle_Height;
            int t_wr = ts.Triangle_Width;
            int n1_hr = ts.Noise1_Height;
            int n1_wr = ts.Noise1_Width;
            int n2_hr = ts.Noise2_Height;
            int n2_wr = ts.Noise2_Width;
            int offset = ts.Offset_From_Zero;

            BackgroundLeft = p_BackgroundLeft;
            BackgroundRight = p_BackgroundRight;
            int TwoTimesEdgeOffset = 2 * EdgeOffset;
            Heights = new int[(BackgroundLeft.Width + BackgroundRight.Width) - TwoTimesEdgeOffset];
            
            Color[] MiniColorData = new Color[p_MiniMap.Height * p_MiniMap.Width];
            p_MiniMap.GetData<Color>(MiniColorData);
            float pi = 3.14159265f;
            //Color[] colordata = new Color[BackgroundLeft.Height * BackgroundLeft.Width];
            //BackgroundLeft.GetData<Color>(colordata);

            StorageDevice storageDevice = Game1.Instance.StorageDevice;
            StorageContainer storageContainer = storageDevice.OpenContainer("Name");
            string filename = Path.Combine(storageContainer.Path, "PreviouslyPlayed.txt");
            FileStream stream = File.Open(filename, FileMode.Append, FileAccess.Write);
            StreamWriter sw = new StreamWriter(stream);
            ts.Write(sw);
            sw.Flush();
            sw.Close();
            storageContainer.Dispose();

            for (int i = (0 + offset + EdgeOffset); i < (BackgroundLeft.Width + BackgroundRight.Width + offset - EdgeOffset); ++i)
            {

                // Change heights and widths to adjust shape of dynamically generated terrain. Widths cannot be zero, but heights can be.

                int square_height = 75 + s_hr, square_width = 10 + s_wr;
                double square = ((square_height) * (Math.Sin((double)0.01 / square_width * 2 * pi * i))) + ((square_height / 3) * (Math.Sin((double)0.01 / square_width * 6 * pi * i))) + ((square_height / 5) * (Math.Sin((double)0.01 / square_width * 10 * pi * i))) + ((square_height / 7) * (Math.Sin((double)0.01 / square_width * 14 * pi * i))) + ((square_height / 9) * (Math.Sin((double)0.01 / square_width * 18 * pi * i)));

                int triangle_height = 100 + t_hr, triangle_width = 8 + t_wr;
                double triangle = (((triangle_height) * (Math.Sin((double)(0.01 / triangle_width) * 2 * pi * i))) - ((triangle_height / 9) * (Math.Sin((double)(0.01 / triangle_width) * 6 * pi * i))) + ((triangle_height / 25) * (Math.Sin((double)(0.01 / triangle_width) * 10 * pi * i))));

                int sine_height = -50 + n1_hr, sine_width = 40 + n1_wr;
                double sine = ((sine_height) * (Math.Sin((double)(0.01 / sine_width) * 2 * pi * i)));

                int noise_height = -25 + n2_hr, noise_width = 20 + n2_wr;
                double noise = ((noise_height) * (Math.Cos((double)(0.1 / noise_width) * 2 * pi * i)));

                double piece6 = 500;
                double temp = square + triangle + sine + noise + piece6;


                Heights[i - offset - EdgeOffset] = (int)temp;
            }

            /*
            // EdgeOffset
            for (int i = (0 + offset + EdgeOffset); i < (BackgroundLeft.Width + offset); ++i)
            {
                int temp = Heights[i - offset - EdgeOffset];
                for (int j = 0; j < BackgroundLeft.Height; ++j)
                {
                    if (j > temp)
                    {
                        colordata[(i - offset) + j * BackgroundLeft.Width] = new Color(0, 0, 0, 255); //255 means no alpha
                    }
                    else
                    {
                        //colordata[i - offset) + j * p_Texture.Width] = Terrain.BackGround;
                    }

                    if (((i - offset) % 10 == 0) && (j % 10 == 0))
                    {
                        int index = ((i - offset) / 10) + (j * p_MiniMap.Width / 10);
                        MiniColorData[index - (EdgeOffset / 10)] = new Color(35, 15, 15, 255); //255 means no alpha
                        if (!(j > temp))
                        {
                            MiniColorData[index - (EdgeOffset / 10)] = colordata[(i - offset) + j * BackgroundLeft.Width];//Terrain.BackGround;
                        }
                    }

                }
            }
            BackgroundLeft.SetData<Color>(colordata);

            //===========================================================================================

            BackgroundRight.GetData<Color>(colordata);

            int Part2Offset = BackgroundLeft.Width + offset;

            for (int i = (BackgroundLeft.Width + offset); i < ( (BackgroundLeft.Width + offset) + BackgroundRight.Width - EdgeOffset); ++i)
            {
                int temp = Heights[i - offset - EdgeOffset];
                for (int j = 0; j < BackgroundRight.Height; ++j)
                {
                    if (j > temp)
                    {
                       colordata[i - (Part2Offset) + j * BackgroundRight.Width] = new Color(0, 0, 0, 255); //255 means no alpha
                    }
                    //(BackgroundLeft.Width + offset)
                    if (((i) % 10 == 0) && (j % 10 == 0))
                    {
                        int index = ((i - offset - EdgeOffset) / 10) + (j * p_MiniMap.Width / 10);
                        MiniColorData[index] = new Color(35, 15, 15, 255); //255 means no alpha
                        if (!(j > temp))
                        {
                            MiniColorData[index] = colordata[(i - (Part2Offset)) + j * BackgroundRight.Width];//Terrain.BackGround;
                        }
                    }

                }
            }

            BackgroundRight.SetData<Color>(colordata);

            */
            //m_TextureData = colordata;

            for (int i = (0 + offset + EdgeOffset); i < ((p_MiniMap.Width * 10) + offset + EdgeOffset); i += 10)
            {
                int temp = Heights[i - offset - EdgeOffset];
                for (int j = 0; j < (p_MiniMap.Height * 10); j += 10)
                {
                    int index = ((i - offset - EdgeOffset) / 10) + (j * p_MiniMap.Width / 10);
                    if (j > temp)
                    {
                        MiniColorData[index] = Color.Black; //255 means no alpha
                    }
                    else
                    {
                        MiniColorData[index] = Color.MediumPurple;
                    }
                }
            }
            p_MiniMap.SetData<Color>(MiniColorData);
            m_MiniMap = p_MiniMap;

            System.GC.Collect();

            
            m_TextureData = null;

            m_BoundingRectangle = new Rectangle(0, 0, (int)BackgroundLeft.Width, (int)BackgroundLeft.Height);
            m_Transformation = Matrix.CreateTranslation(new Vector3(0.0f, 0.0f, 0.0f));
        }
     
        public int this[int x]
        {
            get
            {
                int TexWidth = (BackgroundLeft.Width + BackgroundRight.Width) - (2 * Terrain.EdgeOffset);
                if(x >= TexWidth ){
                    x = TexWidth - 1;
                }else if(x <= 0){
                    x = 1;
                }
                return Heights[x];
            }
        }


        public void DeformTerrain(List<Vector2> Points)
        {
            BackgroundLeft.GetData<Color>(m_TextureData);
            foreach (Vector2 point in Points)
            {
                m_TextureData[(int)point.X + ((int)point.Y) * BackgroundLeft.Width] = Terrain.BackGround;
            }
            BackgroundLeft.SetData<Color>(m_TextureData);
        }

        public bool IntersectPixels(GameObject lhs, out List<Vector2> Points)
        {
            Collidable temp = (lhs[ComponentType.Collidable] as Collidable);
            return IntersectPixels(m_Transformation, BackgroundLeft.Width, BackgroundLeft.Height, m_TextureData, temp._Transform, temp._width, temp._height, temp._TextureData, out Points);
        }

        /// <summary>
        /// Determines if there is overlap of the non-transparent pixels between two
        /// sprites.
        /// </summary>
        /// <param name="transformA">World transform of the first sprite.</param>
        /// <param name="widthA">Width of the first sprite's texture.</param>
        /// <param name="heightA">Height of the first sprite's texture.</param>
        /// <param name="dataA">Pixel color data of the first sprite.</param>
        /// <param name="transformB">World transform of the second sprite.</param>
        /// <param name="widthB">Width of the second sprite's texture.</param>
        /// <param name="heightB">Height of the second sprite's texture.</param>
        /// <param name="dataB">Pixel color data of the second sprite.</param>
        /// <returns>True if non-transparent pixels overlap; false otherwise</returns>
        public bool IntersectPixels(
                            Matrix transformA, int widthA, int heightA, Color[] dataA,
                            Matrix transformB, int widthB, int heightB, Color[] dataB, out List<Vector2> Points)
        {
            Points = new List<Vector2>();
            // Calculate a matrix which transforms from A's local space into
            // world space and then into B's local space
            Matrix transformAToB = transformA * Matrix.Invert(transformB);

            // When a point moves in A's local space, it moves in B's local space with a
            // fixed direction and distance proportional to the movement in A.
            // This algorithm steps through A one pixel at a time along A's X and Y axes
            // Calculate the analogous steps in B:
            Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
            Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);

            // Calculate the top left corner of A in B's local space
            // This variable will be reused to keep track of the start of each row
            Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);

            // For each row of pixels in A
            for (int yA = 0; yA < heightA; yA++)
            {
                // Start at the beginning of the row
                Vector2 posInB = yPosInB;

                // For each pixel in this row
                for (int xA = 0; xA < widthA; xA++)
                {
                    // Round to the nearest pixel
                    int xB = (int)Math.Round(posInB.X);
                    int yB = (int)Math.Round(posInB.Y);

                    // If the pixel lies within the bounds of B
                    if (0 <= xB && xB < widthB &&
                        0 <= yB && yB < heightB)
                    {
                        // Get the colors of the overlapping pixels
                        Color colorA = dataA[xA + yA * widthA];
                        Color colorB = dataB[xB + yB * widthB];

                        // If both pixels are not completely transparent,
                        if (colorA.A != 0 && colorB.A != 0)
                        {
                            // then an intersection has been found
                            Points.Add(new Vector2(xA, yA));
                        }
                    }

                    // Move to the next pixel in the row
                    posInB += stepX;
                }

                // Move to the next row
                yPosInB += stepY;
            }

            if (Points.Count > 0)
            {
                return true;
            }
            else
            {
                // No intersection found
                return false;
            }
        }

    }
}
